/**
 * 设计一个完善的响应式系统
 * 使用 Set 作为桶的数据结构 (这种做法不完善,有 BUG)
 */

/*
let 不完善的响应系统 = (function () {
// 存储副作用函数的桶
    const bucket = new Set()

// 原始数据
    const data = {text: 'hello world'}

// 用一个全局变量存储被注册的副作用函数
    let activeEffect

// effect 函数用于注册副作用函数
// 这段代码中存在三个角色:
// 1. 被操作 (读取) 的代理对象 obj
// 2. 被操作 (读取) 的字段名 text
// 3. 使用 effect 函数注册的副作用函数 effectFn
    function effect(fn) {
        // 当调用 effect 注册副作用函数时,将副作用函数 fn 赋值给 activeEffect
        activeEffect = fn
        // 执行副作用函数
        fn()
    }

    const obj = new Proxy(data, {
        get(target, key) {
            // 将 activeEffect 中存储的副作用函数集体到“桶”中
            if (activeEffect) {
                bucket.add(activeEffect)
            }
            return Reflect.get(target, key)
        }, set(target, key, newVal) {
            Reflect.set(target, key, newVal)
            bucket.forEach(fn => fn())
            return true
        }
    })()

// 读取或设置 target 上存在的属性的情况
    effect(// 一个匿名的副作用函数
        () => {
            document.body.innerText = obj.text
        })

// 读取或设置 target 上不存在的属性的情况
    effect(// 匿名副作用函数
        () => {
            console.log('effect run') // 会打印 2 次
            document.body.innerText = obj.text
        })

    setTimeout(() => {
        // 副作用函数中并没有读取 notExist 属性的值
        obj.notExit = 'hello vue3'
    }, 1000)
})()
*/


/**
 * 完善的响响应式系统,没有对 get 和 set 的代码进行封装
 * 桶结构使用 WeakMap 代替 Set
 * WeakMap 由 target --> Map 构成
 * Map 由 key --> Set 构成
 */
/*
// 存储副作用函数的桶
const bucket = new WeakMap()

// 原始数据
const data = {text: 'hello world'}

// 用一个全局变量存储被注册的副作用函数
let activeEffect

// effect 函数用于注册副作用函数
// 这段代码中存在三个角色:
// 1. 被操作 (读取) 的代理对象 obj
// 2. 被操作 (读取) 的字段名 text
// 3. 使用 effect 函数注册的副作用函数 effectFn
function effect(fn) {
    // 当调用 effect 注册副作用函数时,将副作用函数 fn 赋值给 activeEffect
    activeEffect = fn
    // 执行副作用函数
    fn()
}

const obj = new Proxy(data, {
    // 拦截读取操作
    get(target, key) {
        // 没有 activeEffect,直播 return
        if (!activeEffect) return
        // 根据 target 从"桶"中取得 depsMap,它也是一个 Map 类型: key --> effects
        let depsMap = bucket.get(target)
        // 如果不存在 depsMap,那么新建一个 Map 并与 target 关联
        if (!depsMap) {
            bucket.set(target, (depsMap = new Map()))
        }
        // 再根据 key 从 depsMap 中取得 deps,它是一个 Set 类型,
        // 里面存储着所有与当前 key 相关联的副作用函数: effects
        let deps = depsMap.get(key)
        // 如果 deps 不存在,同样新建一个 Set 并与 key 关联
        if (!deps) {
            depsMap.set(key, (deps = new Set()))
        }
        // 最后将当前激活的副作用函数添加到桶里
        deps.add(activeEffect)
        return Reflect.get(target, key)
    },
    // 拦截设置操作
    set(target, key, newVal) {
        // 设置属性
        Reflect.set(target, key, newVal)
        // 根据 target 从桶中取得 depsMap,它是 key --> effects
        const depsMap = bucket.get(target)
        if (!depsMap) return
        // 根据 key 取得所有副作用函数 effects
        const effects = depsMap.get(key)
        effects && effects.forEach((fn) => fn())
    }
})

// 读取或设置 target 上存在的属性的情况
effect(// 一个匿名的副作用函数
    () => {
        document.body.innerText = obj.text
    })

// 读取或设置 target 上不存在的属性的情况
effect(// 匿名副作用函数
    () => {
        console.log('effect run') // 会打印 2 次
        document.body.innerText = obj.text
    })

setTimeout(() => {
    // 副作用函数中并没有读取 notExist 属性的值
    obj.notExit = 'hello vue3'
}, 1000)
*/

/**
 * 完善的响应式系统,对 get 和 set 的逻辑进行封装
 * 将 get 拦截函数里的编写把副作用收集到桶里的这部分逻辑单独封装到一个 track 函数中,表达追踪的含义
 * 将 set 拦截函数里的部分逻辑封闭装到 trigger 函数中
 */
// 存储副作用函数的桶
const bucket = new WeakMap()

// 原始数据
const data = {text: 'hello world'}

// 用一个全局变量存储被注册的副作用函数
let activeEffect

// effect 函数用于注册副作用函数
// 这段代码中存在三个角色:
// 1. 被操作 (读取) 的代理对象 obj
// 2. 被操作 (读取) 的字段名 text
// 3. 使用 effect 函数注册的副作用函数 effectFn
function effect(fn) {
    // 当调用 effect 注册副作用函数时,将副作用函数 fn 赋值给 activeEffect
    activeEffect = fn
    // 执行副作用函数
    fn()
}

const obj = new Proxy(data, {
    // 拦截读取操作
    get(target, key) {
        // 将副作用函数 activeEffect 添加到存储副作用函数的桶中
        track(target, key)
        return Reflect.get(target, key)
    },
    // 拦截设置操作
    set(target, key, newVal) {
        // 设置属性
        Reflect.set(target, key, newVal)
        trigger(target,key)
    }
})

// 在 get 拦截函数内调用 track 函数追踪变化
function track(target, key) {
    // 没有 activeEffect,直播 return
    if (!activeEffect) return
    // 根据 target 从"桶"中取得 depsMap,它也是一个 Map 类型: key --> effects
    let depsMap = bucket.get(target)
    // 如果不存在 depsMap,那么新建一个 Map 并与 target 关联
    if (!depsMap) {
        bucket.set(target, (depsMap = new Map()))
    }
    // 再根据 key 从 depsMap 中取得 deps,它是一个 Set 类型,
    // 里面存储着所有与当前 key 相关联的副作用函数: effects
    let deps = depsMap.get(key)
    // 如果 deps 不存在,同样新建一个 Set 并与 key 关联
    if (!deps) {
        depsMap.set(key, (deps = new Set()))
    }
    // 最后将当前激活的副作用函数添加到桶里
    deps.add(activeEffect)
}

// 在 set 拦截函数内调用 trigger 函数触发变化
function trigger(target,key) {
    // 根据 target 从桶中取得 depsMap,它是 key --> effects
    const depsMap = bucket.get(target)
    if (!depsMap) return
    // 根据 key 取得所有副作用函数 effects
    const effects = depsMap.get(key)
    effects && effects.forEach((fn) => fn())
}

// 读取或设置 target 上存在的属性的情况
effect(// 一个匿名的副作用函数
    () => {
        document.body.innerText = obj.text
    })

// 读取或设置 target 上不存在的属性的情况
effect(// 匿名副作用函数
    () => {
        console.log('effect run') // 会打印 2 次
        document.body.innerText = obj.text
    })

setTimeout(() => {
    // 副作用函数中并没有读取 notExist 属性的值
    obj.notExit = 'hello vue3'
}, 1000)
